﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Caching;

namespace LifeFlow.Servicer
{
    internal class ServiceCache
    {
        private const string CacheKey = "LifeFlow.ServiceCache";
        internal MethodInfo GetMethod(string service, string action, ServiceVersion version)
        {
            var cache = GetCache();

            ServiceCacheItem first = null;
            foreach (ServiceCacheItem s in cache)
            {
                var svMin = s.Attribute.MinServiceVersion;
                var svMax = s.Attribute.MaxServiceVersion;
                if (s.Attribute.Service == service && s.Attribute.Action == action &&
                    (ServiceVersion.HasAccess(version, svMin, svMax)))
                {
                    first = s;
                    break;
                }
            }

            if (first == null)
                return null;
            return first.Method;
        }

        internal ServiceAttribute GetAttribute(string service, string action, ServiceVersion version)
        {
            var cache = GetCache();

            ServiceCacheItem first = null;
            foreach (ServiceCacheItem s in cache)
            {
                var svMin = s.Attribute.MinServiceVersion;
                var svMax = s.Attribute.MaxServiceVersion;
                if (s.Attribute.Service == service && s.Attribute.Action == action && 
                    (ServiceVersion.HasAccess(version, svMin, svMax)))
                {
                    first = s;
                    break;
                }
            }

            if (first == null)
                return null;
            return first.Attribute;
        }

        internal List<ServiceCacheItem> GetMethods(string service, ServiceVersion version)
        {
            var cache = GetCache();

            List<ServiceCacheItem> results = new List<ServiceCacheItem>();
            foreach (ServiceCacheItem s in cache)
            {
                var svMin = s.Attribute.MinServiceVersion;
                var svMax = s.Attribute.MaxServiceVersion;
                if (s.Attribute.Service == service &&
                    (ServiceVersion.HasAccess(version, svMin, svMax)))
                {
                    results.Add(s);
                }
            }

            return results;
        }

        internal void CacheServices(string path)
        {
            var files = Directory.GetFiles(path, "*.dll");

            var methods = files.ToList()
                .SelectMany(f =>
                            Assembly.LoadFile(f)
                                .GetTypes().SelectMany(
                                    t =>
                                    t.GetMethods().Where(
                                        m => m.GetCustomAttributes(typeof (ServiceAttribute), true).Any())
                                        .ToList()
                                )

                );

            ClearCache();

            var cache = new List<ServiceCacheItem>(); ;

            methods
                .ToList()
                .ForEach(t =>
                             {
                                 var attrs = t.GetCustomAttributes(typeof (ServiceAttribute), true);

                                 attrs.ToList()
                                     .ForEach(attr => cache.Add(new ServiceCacheItem()
                                                                    {
                                                                        Attribute = (ServiceAttribute) attr,
                                                                        Method = t
                                                                    }));
                             });

            SaveCache(cache);
        }

        internal void ClearCache()
        {
            HttpContext.Current.Cache.Remove(CacheKey);
        }

        internal List<ServiceCacheItem> GetCache()
        {
            var cache = HttpContext.Current.Cache[CacheKey];
            if (cache == null)
            {
                cache = new List<ServiceCacheItem>();
                HttpContext.Current.Cache.Insert(CacheKey, cache);
            }
            return (List<ServiceCacheItem>)cache;
        }

        internal void SaveCache(List<ServiceCacheItem> cache)
        {
            HttpContext.Current.Cache.Insert(CacheKey, cache);
        }

        internal class ServiceCacheItem
        {
            public ServiceAttribute Attribute { get; set; }
            public MethodInfo Method { get; set; }
        }
    }
}
